#ifndef _EPOLL_FD_H_
#define _EPOLL_FD_H_

#ifdef __cplusplus
extern "C"{
#endif

#include "def.h"
#include "io_tools.h" // for fd_set_nonblock()
#include <fcntl.h>
#include <sys/epoll.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#define EPOLL_DEFAULT_SIZE 1024 

typedef struct epoll_fd
{ 
    int epfd;
    size_t size;
    struct epoll_event *events;
}epoll_fd_t;

static inline int epoll_fd_create(epoll_fd_t *self, size_t size)
{
    if(!size)
        size = EPOLL_DEFAULT_SIZE;
    
    self->epfd = epoll_create(size);
    if(self->epfd < 0)
        return -1;
    
    self->size = size;
    self->events = (struct epoll_event *)MALLOC(sizeof(struct epoll_event) * size);
    if(self->events == NULL)
        return -1;

    return 0;
}

static inline int epoll_fd_release(epoll_fd_t *self)
{
    safe_free(self->events);
	self->size = 0;
    safe_close(self->epfd);
    return 0;
}

static inline int epoll_fd_add(epoll_fd_t *self, int fd, int event, void *ptr)
{
    if(fd_set_nonblock(fd) < 0){
        ERR("fd_set_nonblock fail");
        return -1;
    }

    struct epoll_event ev;
    if(ptr)
        ev.data.ptr = ptr;
    else
        ev.data.fd = fd;
    ev.events = event;
    
    if(epoll_ctl(self->epfd, EPOLL_CTL_ADD, fd, &ev) < 0){
        ERR("epoll_ctl_add fail");
        return -1;
    }

    return 0;
}

static inline int epoll_fd_mod(epoll_fd_t *self, int fd, int event, void *ptr)
{
    struct epoll_event ev;
    if(ptr)
        ev.data.ptr = ptr;
    else
        ev.data.fd = fd;
    ev.events = event;
    
    if(epoll_ctl(self->epfd, EPOLL_CTL_MOD, fd, &ev) < 0){
        ERR("epoll_ctl_mod fail");
        return -1;
    }

    return 0;
}

static inline int epoll_fd_del(epoll_fd_t *self, int fd)
{
    if(epoll_ctl(self->epfd, EPOLL_CTL_DEL, fd, NULL) < 0){
        ERR("epoll_ctl_del fail");
        return -1;
    }

    return 0;
}

static inline int epoll_fd_wait(epoll_fd_t *self, int timeout)
{
    return epoll_wait(self->epfd, self->events, self->size, timeout);
}

static inline struct epoll_event *epoll_events(epoll_fd_t *self)
{
    return self->events;
}

#ifdef __cplusplus
}
#endif

#endif
